﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace 实现Unity协程
{
       /// <summary>
     /// 协程类
     /// </summary>
    public sealed class Coroutine
    {
        private Coroutine() { }

        /// <summary>
        /// 协程实例
        /// </summary>
        private static Coroutine instance;

        static Coroutine()
        {
            instance = new Coroutine();
        }

        /// <summary>
        /// 所有的迭代器
        /// </summary>
        private List<IEnumeratorContainer> itors = new List<IEnumeratorContainer>();

        /// <summary>
        /// 启动协程
        /// </summary>
        /// <param name="iCoroutine"></param>
        public static void StartCoroutine(IEnumerator iCoroutine)
        {
            instance.itors.Add(new IEnumeratorContainer(iCoroutine));
        }

        /// <summary>
        /// 更新___主线程逻辑循环调用
        /// </summary>
        public static void Update()
        {

            //循环处理
            int len = instance.itors.Count;
            for (int i = len - 1; i >= 0; i--)
            {
                if (instance.itors[i].Frame())
                {
                    instance.itors.RemoveAt(i);
                }
            }
        }

        /// <summary>
        /// 迭代器容器
        /// </summary>
        private class IEnumeratorContainer
        {
            //按帧延时, 按时间延时

            /// <summary>
            /// 迭代器_自身
            /// </summary>
            private IEnumerator itor;

            /// <summary>
            /// 是否等待
            /// </summary>
            private bool isWait
            {
                get
                {
                    return waitMillisecond > 0 || waitFrame >= 0;
                }
            }

            /// <summary>
            /// 子迭代器
            /// </summary>
            private IEnumeratorContainer childContainer;

            /// <summary>
            /// 等待的秒数
            /// </summary>
            private int waitMillisecond = -1;

            /// <summary>
            /// 等待的帧
            /// </summary>
            private int waitFrame = -1;

            /// <summary>
            /// 开始等待的时间
            /// </summary>
            private DateTime waitStartTime;

            /// <summary>
            /// 创建迭代
            /// </summary>
            /// <param name="itor"></param>
            public IEnumeratorContainer(IEnumerator itor)
            {
                this.itor = itor;
            }

            /// <summary>
            /// 一帧
            /// </summary>
            /// <returns>表示否最后一帧</returns>
            public bool Frame()
            {
                if (isWait)
                {   //处理等待
                    if (waitFrame >= 0)
                    {
                        waitFrame--;
                    }
                    if (waitMillisecond > 0)
                    {
                        TimeSpan ts = DateTime.Now - waitStartTime;
                        if (ts.TotalMilliseconds > waitMillisecond)
                        {
                            waitMillisecond = -1;
                        }
                    }
                }
                if (isWait) //还需要等待
                    return false;


                bool isHave = true;
                if (childContainer != null)
                {//子迭代不为空，出来子迭代
                    if (childContainer.Frame())
                    {//子迭代处理完了
                        childContainer = null;
                        return Frame(); //处理自己
                    }
                }
                else
                {
                    isHave = itor.MoveNext();
                    if (isHave)
                    { //做处理
                        if (itor.Current is IEnumerator)
                        {//当前是个迭代
                            IEnumerator chitor = itor.Current as IEnumerator;
                            childContainer = new IEnumeratorContainer(chitor);
                        }
                        else if (itor.Current is WaitCoroutine)
                        { //当前是等待处理
                            WaitCoroutine wc = itor.Current as WaitCoroutine;
                            AddAttributes(wc);
                        }
                    }
                }
                return !isHave;
            }

            /// <summary>
            /// 添加等待属性
            /// </summary>
            /// <param name="wc"></param>
            private void AddAttributes(WaitCoroutine wc)
            {
                switch (wc.style)
                {
                    case 0:      //秒钟
                        waitMillisecond = wc.value * 1000;
                        waitStartTime = DateTime.Now;
                        break;
                    case 1:     //毫秒
                        waitMillisecond = wc.value;
                        waitStartTime = DateTime.Now;
                        break;
                    case 2:
                        waitFrame = wc.value;
                        break;
                }
            }

            /// <summary>
            /// 添加儿子
            /// </summary>
            private void AddChild(IEnumerator _itor)
            {
                childContainer = new IEnumeratorContainer(_itor);
            }

        }
    }

    /// <summary>
    /// 等待基类
    /// </summary>
    public abstract class WaitCoroutine
    {
        /// <summary>
        /// 类型 0等待 秒种  1等待毫秒值   2等待帧数
        /// </summary>
        public int style;

        /// <summary>
        /// 数值
        /// </summary>
        public int value;
    }

    /// <summary>
    /// 等待秒
    /// </summary>
    public class WaitSecond : WaitCoroutine
    {
        /// <summary>
        /// 秒钟
        /// </summary>
        /// <param name="second"></param>
        public WaitSecond(int second)
        {
            style = 0;
            value = second;
        }
    }

    /// <summary>
    /// 等待毫秒值 
    /// </summary>
    public class WaitMillisecond : WaitCoroutine
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="millisecond"></param>
        public WaitMillisecond(int millisecond)
        {
            style = 1;
            value = millisecond;
        }
    }

    /// <summary>
    /// 等待帧数
    /// </summary>
    public class WaitFrame : WaitCoroutine
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="frame"></param>
        public WaitFrame(int frame)
        {
            style = 2;
            value = frame;
        }
    }
}
